<!--
 * @Author: 小花
 * @Date: 2022-05-24 22:09:01
 * @LastEditors: 小花
 * @LastEditTime: 2022-05-28 18:01:28
 * @FilePath: /permission_client/src/views/UserView.vue
 * @Description: 用户管理
-->
<template>
  <el-button type="primary" @click="openDialog()">
    <font-awesome-icon icon="user-plus" />添加用户
  </el-button>
  <el-tag class="countBox">
    <span>已选择 <span class="countStyle">{{ multipleSelection.length }}</span> 项</span>
    <el-link type="primary" @click="toggleSelection()" :underline="false">
      <font-awesome-icon icon="eraser" />清空
    </el-link>
    <el-link type="danger" v-if="multipleSelection.length" @click="execdelUser()" :underline="false">
      <font-awesome-icon icon="trash-can" />批量删除
    </el-link>
  </el-tag>
  <el-table ref="multipleTableRef" :data="filterUserList" @selection-change="handleSelectionChange"
    :header-cell-style="{ background: '#eef1f6', color: '#606266' }" border style="width: 100%">
    <el-table-column type="selection" width="55" />
    <el-table-column prop="account" align="center" label="账号" width="180" />
    <el-table-column prop="username" align="center" label="用户名" width="100" />
    <el-table-column prop="avatar" align="center" label="头像" width="80">
      <template #default="scope">
        <el-avatar :src="AxiosBaseURL + scope.row.avatar" @error="() => true">
          <font-awesome-icon icon="user" />
        </el-avatar>
      </template>
    </el-table-column>
    <el-table-column prop="password" align="center" label="密码" width="180" />
    <el-table-column prop="phone" align="center" label="电话" width="180" />
    <el-table-column prop="email" align="center" label="邮箱" width="180" />
    <el-table-column prop="status" align="center" label="状态" width="80">
      <template #default="scope">
        <el-tag :type="EStatus.find(it => it.value === scope.row.status)?.type" class="mx-1" effect="dark">
          {{ EStatus.find(it => it.value === scope.row.status)?.label }}
        </el-tag>
      </template>
    </el-table-column>
    <el-table-column prop="createBy" align="center" label="创建者" width="100" />
    <el-table-column prop="createTime" align="center" label="创建时间" width="180" />
    <el-table-column prop="updateBy" align="center" label="更新者" width="100" />
    <el-table-column prop="updateTime" align="center" label="更新时间" width="180" />
    <el-table-column fixed="right" width="180">
      <template #header>
        <el-input v-model="tableSearchValue" size="small" placeholder="搜索用户名" />
      </template>
      <template #default="scope">
        <div class="rightFixed">
          <el-link type="primary" @click="editUser(scope.row)" :underline="false">编辑</el-link>
          <el-dropdown>
            <span class="el-dropdown-link">
              更多
              <font-awesome-icon icon="chevron-down" />
            </span>
            <template #dropdown>
              <el-dropdown-menu>
                <el-dropdown-item @click="upUserStatus(scope.row)">
                  {{ EStatus.find(it => it.value === scope.row.status)?.setStatus }}
                </el-dropdown-item>
                <el-dropdown-item @click="execdelUser(scope.row)">
                  删除
                </el-dropdown-item>
              </el-dropdown-menu>
            </template>
          </el-dropdown>
        </div>
      </template>
    </el-table-column>
  </el-table>
  <el-pagination style="display: flex;justify-content: center;margin: 1rem 0;
  align-items: center;" @size-change="(val: number) => pagination.pageSize = val"
    @current-change="(val: number) => pagination.currentPage = val" :current-page="pagination.currentPage"
    :pager-count="pagination.pagerCount" :page-sizes="pagination.pageSizes" :page-size="pagination.pageSize"
    :total="pagination.total" background layout="total, sizes, prev, pager, next, jumper">
  </el-pagination>

  <el-dialog v-model="dialogFormVisible" :title="dialogTitle.title" :before-close="beforeClose">
    <el-form :model="form" :rules="rules" ref="ruleFormRef" :label-position="'right'" label-width="6rem">
      <el-form-item label="账号" prop="account">
        <el-input v-model="form.account" placeholder='请输入账号' />
      </el-form-item>
      <el-form-item label="用户名" prop="username">
        <el-input v-model="form.username" placeholder='请输入用户名' />
      </el-form-item>
      <el-form-item label="密码" prop="password">
        <el-input v-model="form.password" placeholder='请输入密码' />
      </el-form-item>
      <el-form-item label="头像" v-if="dialogTitle.flag">
        <el-upload class="avatar-uploader" action="" list-type="picture-card" :auto-upload="false"
          accept=".jpg,.jpeg,.png,.JPG,.JPEG" :limit="1" :on-remove="handleRemove" :on-change="handleAvatarChangeIcon">
          <el-image :src="AxiosBaseURL + form.avatar">
            <template #error>
              <div class="image-slot">
                <font-awesome-icon icon="plus" />
              </div>
            </template>
          </el-image>
        </el-upload>
      </el-form-item>
      <el-form-item label="角色" prop="roleIds">
        <el-select v-model="form.roleIds" multiple placeholder="用户角色" style="width: 100%">
          <el-option v-for="item in roles" :key="item.id" :label="item.roleName" :value="item.id" />
        </el-select>
      </el-form-item>
      <el-form-item label="电话" prop="phone">
        <el-input v-model="form.phone" placeholder='请输入电话' />
      </el-form-item>
      <el-form-item label="邮箱" prop="email">
        <el-input v-model="form.email" placeholder='请输入邮箱' />
      </el-form-item>
      <el-form-item label="状态">
        <el-switch v-model="form.status" active-text="冻结" inactive-text="正常" active-color="#C0CCDA"
          inactive-color="#409EFF" />
      </el-form-item>
    </el-form>
    <template #footer>
      <span class="dialog-footer">
        <el-button @click="resetForm(ruleFormRef)">Cancel</el-button>
        <el-button type="primary" @click="submitForm(ruleFormRef)">Confirm</el-button>
      </span>
    </template>
  </el-dialog>
</template>

<script lang='ts'>
import { IRole } from '@/model/RoleView'
import UserView, { dialogTemplate, EStatus, IUser, IUserRole, rules } from '@/model/UserView'
import { delUser, pullAllRole, pullAllUser, pullAllUserRole, updateUser, uploadAvatar } from '@/request'
import axios from '@/request/axios'
import { responseEnum, responseUtil } from '@/request/responseUtil'
import { useStore } from '@/store'
import { DialogBeforeCloseFn, ElMessage, FormInstance, UploadFile } from 'element-plus'
import { computed, defineComponent, reactive, toRefs } from 'vue'

export default defineComponent({
  name: 'UserView',
  setup () {
    const store = useStore()
    const coreData = reactive(new UserView())
    const AxiosBaseURL = axios.defaults.baseURL

    /**
     * @description: 后端获取所有用户
     * @param {*}
     * @return {*}
     */
    const execpullAllUser = async () => {
      const result = await (await pullAllUser()).data as responseUtil<IUser[]>
      if (result.code === responseEnum.OK) {
        coreData.userList = result.data || []
        coreData.pagination.total = coreData.userList.length
      }
    }

    let roles = store.roles

    /**
     * @description: 后端获取角色列表
     * @param {*}
     * @return {*}
     */
    const execpullAllRole = async () => {
      if (!store.roles.length) {
        store.roles = (await (await pullAllRole()).data as responseUtil<IRole[]>).data
        roles = store.roles
      }
    }

    /**
     * @description: 获取用户角色列表
     * @param {*}
     * @return {*}
     */
    const execpullAllUserRole = async () => {
      const result = await (await pullAllUserRole()).data as responseUtil<IUserRole[]>
      if (result.code === responseEnum.OK) {
        coreData.userRoleList = result.data || []
      }
    }

    execpullAllUser()
      .then(() => execpullAllRole())
      .then(() => execpullAllUserRole())

    /**
     * @description: 分页过滤器
     * @param {*} computed
     * @return {*}
     */
    const filterUserList = computed(() => {
      const data = coreData.userList
      const filter = data.filter(item => item.username.toUpperCase().includes(coreData.tableSearchValue.toUpperCase()))
      return filter.slice((coreData.pagination.currentPage - 1) * coreData.pagination.pageSize, coreData.pagination.currentPage * coreData.pagination.pageSize)
    })

    /**
     * @description: 更新用户状态
     * @param {*} row
     * @return {*}
     */
    const upUserStatus = async (row: IUser) => {
      const copy = JSON.parse(JSON.stringify(row)) as IUser
      copy.status = !copy.status
      const result = (await updateUser(copy)).data as responseUtil
      if (result.code === responseEnum.OK) {
        const findIndex = coreData.userList.findIndex(it => it.id === copy.id)
        coreData.userList[findIndex] = result.data
        ElMessage.success(result.msg)
      } else {
        ElMessage.error(result.msg)
      }
    }

    /**
     * @description: 多选框
     * @param {*} val
     * @return {*}
     */
    const handleSelectionChange = (val: IUser[]) => {
      coreData.multipleSelection = val
    }

    /**
     * @description: 表格多选 选择器开开关
     * @param {*} rows
     * @return {*}
     */
    const toggleSelection = (rows?: IUser[]) => {
      if (coreData.multipleTableRef) {
        if (rows) {
          rows.forEach((row) => coreData.multipleTableRef.toggleRowSelection(row, false))
        } else {
          coreData.multipleTableRef.clearSelection()
        }
      }
    }

    /**
     * @description: 删除用户 处理器
     * @param {*} row
     * @return {*}
     */
    const execdelUser = async (row?: IUser) => {
      const idList = [] as string[]
      if (row?.id) {
        idList.push(row.id)
      } else {
        coreData.multipleSelection.forEach(it => {
          if (it.id) idList.push(it.id)
        })
      }
      const result = (await delUser(idList)).data as responseUtil
      if (result.code === responseEnum.OK) {
        idList.forEach(it => {
          coreData.userList.splice(coreData.userList.findIndex(k => k.id === it), 1)
        })
        ElMessage.success(result.msg)
      } else {
        ElMessage.error(result.msg)
      }
    }

    /**
     * @description: 关闭编辑对话框 数据清空
     * @param {*} done 执行关闭
     * @return {*}
     */
    const beforeClose: DialogBeforeCloseFn = (done) => {
      resetForm(coreData.ruleFormRef)
      done()
    }

    /**
     * @description: 对话框表单清除
     * @param {*} formEl
     * @return {*}
     */
    const resetForm = (formEl: FormInstance | undefined) => {
      if (!formEl) return
      formEl.resetFields()
    }

    /**
     * @description: 编辑/更新对话框规则校验器
     * @param {*} formEl
     * @return {*}
     */
    const submitForm = async (formEl: FormInstance | undefined) => {
      if (!formEl) return
      await formEl.validate((valid, fields) => {
        if (valid) {
          execupdateUser()
        } else {
          ElMessage.error(`error submit!, ${fields}`)
        }
      })
    }

    /**
     * @description: 更新/添加用户信息
     * @param {*}
     * @return {*}
     */
    const execupdateUser = async () => {
      await saveUserAvatar()

      const result = await (await updateUser(coreData.form)).data as responseUtil
      if (result.code === responseEnum.OK) {
        if (!coreData.dialogTitle.flag) {
          coreData.userList.push(result.data)
        } else {
          const findIndex = coreData.userList.findIndex(it => it.id === coreData.form.id)
          coreData.userList[findIndex] = result.data
        }
        coreData.dialogFormVisible = false
        resetForm(coreData.ruleFormRef)
        ElMessage.success(result.msg)
        resetForm(coreData.ruleFormRef)
      } else {
        ElMessage.error(result.msg)
      }
    }

    /**
     * @description: 添加新用户-打开添加对话框
     * @param {*}
     * @return {*}
     */
    const openDialog = () => {
      coreData.dialogTitle = dialogTemplate.add
      coreData.dialogFormVisible = true
    }

    /**
     * @description: 编辑用户-打开编辑对话框
     * @param {*} row
     * @return {*}
     */
    const editUser = (row: IUser) => {
      coreData.dialogTitle = dialogTemplate.edit
      coreData.form = JSON.parse(JSON.stringify(row))
      // 查找当前用户所对应的角色
      coreData.form.roleIds = coreData.userRoleList.filter(it => it.userId === coreData.form.id).map(it => it.roleId)
      coreData.dialogFormVisible = true
    }

    /**
     * @description: 文件状态改变时的钩子，添加文件、上传成功和上传失败时都会被调用
     * @param {UploadFile} uploadFile
     * @return {*}
     */
    const handleAvatarChangeIcon = (uploadFile: UploadFile) => {
      if (!uploadFile.raw) return
      if (!['image/jpg', 'image/jpeg', 'image/png'].includes(uploadFile.raw.type)) {
        ElMessage.error('头像图片必须为jpg、png格式!')
      } else if (uploadFile.raw.size / 1024 / 1024 > 2) {
        ElMessage.error('头像图片大小不能超过2MB!')
      }
      coreData.uploadFile = uploadFile.raw as File
    }

    /**
     * @description: 移除上传列表中的图片
     * @param {*}
     * @return {*}
     */
    const handleRemove = () => {
      coreData.uploadFile = {} as File
    }

    /**
     * @description: 保存用户头像信息
     * @param {*}
     * @return {*}
     */
    const saveUserAvatar = async () => {
      if (!coreData.uploadFile) return
      const config = {
        timeout: 30000,
        headers: {
          'Content-Type': 'multipart/form-data'
        }
      }
      const formData = new FormData()
      formData.append('file', coreData.uploadFile)
      formData.append('mainPath', coreData.form.id || '')

      const result = await (await uploadAvatar(formData, config)).data as responseUtil<string>
      if (result.code === responseEnum.OK) {
        coreData.form.avatar = result.data
        coreData.uploadFile = {} as File
      }
    }

    return {
      ...toRefs(coreData),
      filterUserList,
      EStatus,
      rules,
      AxiosBaseURL,
      roles,
      upUserStatus,
      handleSelectionChange,
      toggleSelection,
      execdelUser,
      resetForm,
      submitForm,
      openDialog,
      editUser,
      beforeClose,
      handleAvatarChangeIcon,
      handleRemove
    }
  }
})
</script>

<style lang='scss' scoped>
.avatar-uploader {

  :deep(.el-upload) {
    width: 4rem;
    height: 4rem;
    border: 1px dashed var(--el-border-color);
    border-radius: 6px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
    transition: var(--el-transition-duration-fast);

    &:hover {
      border-color: var(--el-color-primary);

      path {
        fill: #409EFF
      }
    }

    .image-slot {
      display: flex;
    }
  }

  svg {
    font-size: 1.5rem;
    fill: none;
  }

  .avatar {
    width: 100%;
    height: 100%;
    display: block;
  }
}

.el-tag.countBox {
  width: 100%;
  height: 2rem;
  text-align: left;
  display: flex;
  justify-content: left;
  align-items: center;
  color: gray;
  font-weight: bold;
  font-size: 14px;
  padding: 1.2rem 2rem;
  margin: 0.6rem 0;

  .el-link {
    margin-left: 1rem;
  }

  .countStyle {
    color: #409EFF;
  }
}

.rightFixed {
  display: flex;
  align-items: center;

  .el-dropdown-link {
    cursor: pointer;
    color: var(--el-color-primary);
  }

  .el-link {
    margin-right: 1rem;
  }
}
</style>
